#include "../Misc/part2-3_shared.h"

#define NUMBER_OF_ENTRIES 65536
#define NUMBER_OF_PAIRS_PER_BLOCK 10
#define SEED (0x1100220033004400)

int main(int argc, char **argv) {
	
	unsigned char main_seed[20] = "bjdshf";
	char *hash = argv[1];
	char *prefix = argv[2];
	DEHT *deht;
	char **lexicon;
	LONG_INDEX_PROJ lexicon_length;
	char *lexfile_path = argv[3];
	char *rule_as_string = "#^1 + *1@ + %2";
	char mypassword[MAX_PASSWORD_LENGTH];
	LONG_INDEX_PROJ i, quit = 0, k;
	RULE rule;
	char password[MAX_PASSWORD_LENGTH];
	unsigned char binary_hashed_passowrd[MAX_HASH_SIZE];
	BasicHashFunctionPtr hash_function;
	int hash_func_size_in_bytes;
	int status;
	int deht_key_length;
	LONG_INDEX_PROJ passwords_matched = 0;
	FILE *fp;
	
	if (argc !=4) {
		fprintf(stderr ,"Error: Usage test_part2 <hash function name> <deht prefix name> <lexicon path>\n");
		exit(1);
	}

	fp = fopen("D:/part2_deht_passwords.txt", "w");

	printf("loading lexicon from file...\n");
	lexicon = load_lexicon(lexfile_path,&lexicon_length);

	printf("creating rule...\n");
	status = create_rule(&rule,rule_as_string,lexicon,lexicon_length);
	if (status == -1) {
		exit(1);
	}

	if (!strcmp(argv[1], "MD5")) {
		hash_function = MD5BasicHash;
		hash_func_size_in_bytes = MD5_OUTPUT_LENGTH_IN_BYTES;
	} else if (!strcmp(argv[1], "SHA1")) {
		hash_function = SHA1BasicHash;
		hash_func_size_in_bytes = SHA1_OUTPUT_LENGTH_IN_BYTES;
	} else {
		fprintf(stderr, "Error: Hash %s is not supported\n", hash);
		exit(1);
	}

	deht = create_empty_DEHT(prefix, cryptHashToTable, cryptHashTo64bit, hash,
			NUMBER_OF_ENTRIES, NUMBER_OF_PAIRS_PER_BLOCK, BYTES_PER_VALIDATION_KEY);

	if (deht == NULL) {
		free_lexicon(lexicon,lexicon_length);
		free_rule(&rule);
		exit(1);
	}

	printf("adding all passwords from rule %s to DEHT...\n", rule_as_string);
	for (i = 0 ; i < rule.number_of_possible_passwords ; i++) {
		generate_kth_password(i,password,rule);
		cryptHash(hash_function,password,binary_hashed_passowrd);
		fprintf(fp,"%s\n", password);
		status = add_DEHT(deht,binary_hashed_passowrd,hash_func_size_in_bytes,(unsigned char *)password,strlen(password));
		if (status == DEHT_STATUS_FAIL) {
			free_lexicon(lexicon,lexicon_length);
			free_rule(&rule);
			lock_DEHT_files(deht);
			exit(1);
		}
	}
	fclose(fp);

	password[0] = '\0';
	lock_DEHT_files(deht);

	printf("loading DEHT from files...\n");
	deht = load_DEHT_from_files(prefix, cryptHashToTable, cryptHashTo64bit);

	if (!strcmp((deht->header.sHashName),"MD5")) {
		deht_key_length = MD5_OUTPUT_LENGTH_IN_BYTES;
	}
	else {
		deht_key_length = SHA1_OUTPUT_LENGTH_IN_BYTES;
	}

	if (deht == NULL) {
		exit(1);
	}

	printf("querying 50000 passwords...\n");
	while (quit != 50000) {
		k = pseudo_random_function(main_seed, sizeof(LONG_INDEX_PROJ), quit) % rule.number_of_possible_passwords;
		generate_kth_password(k, mypassword, rule);
		cryptHash(hash_function,mypassword,binary_hashed_passowrd);
		status = query_DEHT(deht, binary_hashed_passowrd, deht_key_length, (unsigned char*)password, MAX_PASSWORD_LENGTH);
		if (status == DEHT_STATUS_FAIL) {
			lock_DEHT_files(deht);
			exit(1);
		}
		else password[status]='\0';
		if (!strcmp(mypassword,password)) {
			passwords_matched++;
			mypassword[0] = '\0';
		}
		quit++;
	}

	if (passwords_matched != 50000) {
		printf("test failed : did not find match for 50000 passwords\n");
		exit(1);
	}
	

	quit = 0;
	passwords_matched = 0;
	printf("querying all passwords...\n");
	while (quit != rule.number_of_possible_passwords) {
		k = pseudo_random_function(main_seed, sizeof(LONG_INDEX_PROJ), quit) % rule.number_of_possible_passwords;
		generate_kth_password(k, mypassword, rule);
		cryptHash(hash_function,mypassword,binary_hashed_passowrd);
		
		status = query_DEHT(deht, binary_hashed_passowrd, deht_key_length, (unsigned char*)password, MAX_PASSWORD_LENGTH);
		if (status == DEHT_STATUS_FAIL) {
			lock_DEHT_files(deht);
			exit(1);
		}
		else password[status]='\0';
		if (!strcmp(mypassword,password)) {
			passwords_matched++;
			mypassword[0] = '\0';
		}
		quit++;
	}
	
	if (passwords_matched != rule.number_of_possible_passwords) {
		printf("test failed : did not find match for all passwords\n");
		exit(1);
	}
	printf("Test passed\n");
	return 1;
}